<?php

namespace app\common\utils;

use app\model\Config;
use think\facade\Cache;
use think\facade\Env;

/**
 * 微信支付
 */
class Wechat
{
    private $app_id;
    private $mch_id;
    private $key;
    private $notify;

    /**
     * Wechat constructor.
     * @param $app_id
     * @param $mch_id
     * @param $key
     */
    public function __construct($app_id, $mch_id, $key)
    {
        $this->app_id = $app_id;
        $this->mch_id = $mch_id;
        $this->key = $key;
        return $this;
    }

    public function payCode($order)
    {
        $time = time();
        $input = array(
            'appid' => $this->app_id,
            'mch_id' => $this->mch_id,
            'openid' => $order['openid'],
            'device_info' => 'WEB',
            'nonce_str' => $this->getNonceStr(),
            'body' => $order['transaction'],
            'attach' => $order['transaction'],
            'out_trade_no' => $order['transaction'] . $this->getNonceStr(4),
            'total_fee' => $order['price'] * 100,
            'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
            'time_start' => date("YmdHis", $time),
            'time_expire' => date("YmdHis", $time + 300),
            'notify_url' => $this->notify ? $this->notify : $order['notify'],
            'trade_type' => 'JSAPI',
            'receipt' => 'Y'
        );
        $sign = $this->MakeSign($input);
        $input['sign'] = $sign;
        $order = $this->unifiedOrder($input);
        $parameters = $this->GetJsApiParameters($order);
        return $parameters;
    }

    public function respond()
    {
        $msg = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        $xml = file_get_contents("php://input");
        try {
            libxml_disable_entity_loader(true);
            $result = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        } catch (WxPayException $e) {
            return array('order' => '', 'msg' => '', 'transaction_id' => '');
        }
        if (!is_array($result) || !array_key_exists("transaction_id", $result)) {
            return array('order' => '', 'msg' => '', 'transaction_id' => '');
        }
        $info = array(
            'appid' => $this->app_id,
            'mch_id' => $this->mch_id,
            'transaction_id' => $result['transaction_id'],
            'nonce_str' => $this->getNonceStr()
        );
        $sign = $this->MakeSign($info);
        $info['sign'] = $sign;
        if (!$this->orderQuery($info)) {
            return array('order' => '', 'msg' => '', 'transaction_id' => '');
        } else {
            return array('order' => $result['attach'], 'msg' => $msg, 'transaction_id' => $result['transaction_id']);
        }
    }

    private function orderQuery($order)
    {
        $url = "https://api.mch.weixin.qq.com/pay/orderquery";
        $xml = $this->ToXml($order);
        $return = $this->post($url, $xml);
        libxml_disable_entity_loader(true);
        $result = json_decode(json_encode(simplexml_load_string($return, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        if (array_key_exists("return_code", $result)
            && array_key_exists("result_code", $result)
            && $result["return_code"] == "SUCCESS"
            && $result["result_code"] == "SUCCESS") {
            return true;
        }
        return false;
    }

    public function refund($order)
    {
        $input = array(
            'appid' => $this->app_id,
            'mch_id' => $this->mch_id,
            'nonce_str' => $this->getNonceStr(),
            'transaction_id' => $order['transaction_id'],
            'out_refund_no' => $order['transaction'] . $this->getNonceStr(4),
            'total_fee' => $order['price'] * 100,
            'refund_fee' => $order['price'] * 100,
            'notify_url' => $this->notify ? $this->notify : $order['notify'],
        );
        $sign = $this->MakeSign($input);
        $input['sign'] = $sign;
        $url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
        $xml = $this->ToXml($input);
        $return = $this->post($url, $xml, true);
        try {
            libxml_disable_entity_loader(true);
            $result = json_decode(json_encode(simplexml_load_string($return, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        } catch (WxPayException $e) {
            return array('order' => '', 'refund_id' => '');
        }
        if (array_key_exists("return_code", $result) && $result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
            return array('order' => substr($result['out_refund_no'], 0, -4), 'refund_id' => $result['refund_id']);
        }
        return array('order' => '','msg'=>$result['err_code_des'], 'refund_id' => '');
    }

    public function refundRespond()
    {
        $msg = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        $xml = file_get_contents("php://input");
        try {
            libxml_disable_entity_loader(true);
            $result = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        } catch (WxPayException $e) {
            return array('order' => '', 'msg' => '', 'refund_id' => '');
        }
        if (!is_array($result) || !array_key_exists("return_code", $result)) {
            return array('order' => '', 'msg' => '', 'refund_id' => '');
        }
        $key = Config::getConfigValue('wechat_key');
        $decrypt = base64_decode($result['req_info'], true);
        $decrypt = openssl_decrypt($decrypt, 'aes-256-ecb', md5($key), OPENSSL_RAW_DATA);
        if ($decrypt) {
            $result = json_decode(json_encode(simplexml_load_string($decrypt, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
            return array('order' => substr($result['out_refund_no'], 0, -4), 'msg' => $msg, 'refund_id' => $result['refund_id']);
        } else {
            return array('order' => '', 'msg' => '', 'refund_id' => '');
        }
    }

    /**
     * 产生随机字符串，不长于32位
     * @param int $length
     * @return string
     */
    private function getNonceStr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 生成签名
     * @param $values
     * @return string
     */
    private function MakeSign($values)
    {
        ksort($values);
        $string = $this->ToUrlParams($values);
        $string = $string . "&key=" . $this->key;
        $string = md5($string);
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 格式化参数格式化成url参数
     * @param $values
     * @return string
     */
    private function ToUrlParams($values)
    {
        $buff = "";
        foreach ($values as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }
        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     * 统一下单
     * @param $order
     * @return mixed
     */
    private function unifiedOrder($order)
    {
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $xml = $this->ToXml($order);
        $return = $this->post($url, $xml);
        libxml_disable_entity_loader(true);
        $result = json_decode(json_encode(simplexml_load_string($return, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $result;
    }

    /**
     * 转化数组为xml字符串
     * @param $values
     * @return string
     */
    private function ToXml($values)
    {
        if (!is_array($values) || count($values) <= 0) {
            return '';
        }
        $xml = "<xml>";
        foreach ($values as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * 发送post请求
     * @param $url
     * @param array $data
     * @param bool $cert
     * @return mixed|string
     */
    private function post($url, $data = array(), $cert = false)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        if ($cert) {
            curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLCERT, Env::get('root_path') . 'cert/apiclient_cert.pem');
            curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLKEY, Env::get('root_path') . 'cert/apiclient_key.pem');
        }
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        $tmpInfo = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($httpCode == 200) {
            return $tmpInfo;
        } else {
            return '';
        }
    }

    private function GetJsApiParameters($UnifiedOrderResult)
    {
        if (!array_key_exists("appid", $UnifiedOrderResult)
            || !array_key_exists("prepay_id", $UnifiedOrderResult)
            || $UnifiedOrderResult['prepay_id'] == "") {
            return $this->notify;
        }
        $js_api = array();
        $js_api['appId'] = $UnifiedOrderResult["appid"];
        $timeStamp = time();
        $js_api['timeStamp'] = (string)$timeStamp;
        $js_api['nonceStr'] = $this->getNonceStr();
        $js_api['package'] = 'prepay_id=' . $UnifiedOrderResult['prepay_id'];
        $js_api['signType'] = 'MD5';
        $js_api['paySign'] = $this->MakeSign($js_api);
        return $js_api;
    }
}



